home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2000 #4 / Amiga Plus CD - 2000 - No. 4.iso / Vollversion / CamD / development / devs / midi / checkpoint.asm < prev    next >
Assembly Source File  |  2000-05-15  |  16KB  |  682 lines

  1. ************************************************************************
  2. *                             C. A. M. D.                               *
  3. ************************************************************************
  4. * CMU Amiga Midi Driver - Carnegie Mellon University                   *
  5. * 1988                    - Commodore Amiga                               *
  6. *                                                                       *
  7. * Design & Development    - Roger B. Dannenberg                           *
  8. *                        - Jean-Christophe Dhellemmes                   *
  9. *                        - Bill Barton                                   *
  10. * Copyright 1989 Carnegie Mellon University                               *
  11. ************************************************************************
  12. *
  13. * checkpoint.asm - CAMD MIDI Device Driver for Checkpoint serial card.
  14. *
  15. * Copyright 1990 Sylvan Technical Arts
  16. *
  17. ************************************************************************
  18. * Date          | Change
  19. *-----------------------------------------------------------------------
  20. * 26-Apr-1990 : Created (Joe Pearce)
  21. ************************************************************************
  22.  
  23.         nolist
  24.             include "exec/types.i"
  25.             include "exec/execbase.i"
  26.             include "hardware/custom.i"
  27.             include "hardware/intbits.i"
  28.             include "midi/devices.i"
  29.             include "libraries/configvars.i"
  30.             include "cps.i"
  31.         list
  32.  
  33. JSRLIB        macro
  34.             xref    _LVO\1
  35.             jsr        _LVO\1(a6)
  36.             endm
  37.  
  38. DISABLE        MACRO
  39.         IFC     '\1',''
  40.             MOVE.W  #$04000,custom+intena
  41.             ADDQ.B  #1,IDNestCnt(A6)
  42.          ENDC
  43.          IFNC    '\1',''
  44.             MOVE.L  4,\1
  45.             MOVE.W  #$04000,custom+intena
  46.             ADDQ.B  #1,IDNestCnt(\1)
  47.          ENDC
  48.              ENDM
  49.  
  50. ENABLE      MACRO
  51.         IFC     '\1',''
  52.             SUBQ.B  #1,IDNestCnt(A6)
  53.             BGE.S   ENABLE\@
  54.             MOVE.W  #$0C000,custom+intena
  55. ENABLE\@:
  56.         ENDC
  57.         IFNC    '\1',''
  58.             MOVE.L  4,\1
  59.             SUBQ.B  #1,IDNestCnt(\1)
  60.             BGE.S   ENABLE\@
  61.             MOVE.W  #$0C000,custom+intena
  62. ENABLE\@:
  63.         ENDC
  64.             ENDM
  65.  
  66. Version        equ        0
  67. Revision    equ        1
  68. Ports        equ        2
  69.  
  70. AbsExecBase    EQU        4
  71. custom        EQU        $00dff000
  72.  
  73.             section driver,code
  74.  
  75. ****************************************************************
  76. *
  77. *   Standard MIDI Device driver header
  78. *
  79. ****************************************************************
  80.  
  81. ; code at start of file in case anyone tries to execute us as a program
  82.  
  83.             entry    FalseStart
  84. FalseStart
  85.             moveq    #-1,d0
  86.             rts
  87.  
  88. MDD ; struct MidiDeviceData
  89.             dc.l    MDD_Magic    ; mdd_Magic
  90.             dc.l    Name        ; mdd_Name
  91.             dc.l    IDString    ; mdd_IDString
  92.             dc.w    Version     ; mdd_Version
  93.             dc.w    Revision    ; mdd_Revision
  94.             dc.l    Init        ; mdd_Init
  95.             dc.l    Expunge     ; mdd_Expunge
  96.             dc.l    OpenPort    ; mdd_OpenPort
  97.             dc.l    ClosePort    ; mdd_ClosePort
  98.             dc.b    Ports        ; mdd_NPorts
  99.             dc.b    0            ; mdd_Flags
  100.  
  101. Name        dc.b    'Checkpoint',0
  102. IDString    dc.b    'Checkpoint serial card MIDI device driver',0
  103.             ds.w    0            ; force word alignment
  104.  
  105.  
  106. ****************************************************************
  107. *
  108. *   Serial Port Stuff
  109. *
  110. ****************************************************************
  111.  
  112.         STRUCTURE CPSUnit,0
  113.             ULONG    su_CardBase
  114.             UBYTE    su_pad0
  115.             UBYTE    su_IntMask
  116.             UWORD    su_reserved0
  117.             APTR    su_DataA
  118.             APTR    su_XmitA
  119.             APTR    su_RecvA
  120.             APTR    su_DataB
  121.             APTR    su_XmitB
  122.             APTR    su_RecvB
  123.             LABEL    CPSSubUnit_Size
  124.             APTR    su_MPDA
  125.             APTR    su_MPDB
  126.             LABEL    CPSUnit_Size
  127.  
  128.         STRUCTURE ExtInt,IS_SIZE
  129.             UBYTE    si_IsActive
  130.             UBYTE    si_pad0
  131.             STRUCT    si_Unit0,CPSUnit_Size
  132.             STRUCT    si_Unit1,CPSUnit_Size
  133.             LABEL    ExtInt_Size
  134.  
  135. ;si_CardBase    EQU        su_CardBase
  136. ;si_IntMask    EQU        su_IntMask
  137. ;si_DataA    EQU        su_DataA
  138. ;si_XmitA    EQU        su_XmitA
  139. ;si_RecvA    EQU        su_RecvA
  140. ;si_DataB    EQU        su_DataB
  141. ;si_XmitB    EQU        su_XmitB
  142. ;si_RecvB    EQU        su_RecvB
  143.  
  144. MPD0            ; struct MidiPortData for port A
  145.             dc.l    ActivateXmitA
  146.  
  147. MPD1            ; struct MidiPortData for port B
  148.             dc.l    ActivateXmitB
  149.  
  150. MPD2            ; struct MidiPortData for port C
  151.             dc.l    ActivateXmitC
  152.  
  153. MPD3            ; struct MidiPortData for port D
  154.             dc.l    ActivateXmitD
  155.  
  156.             cnop    0,4
  157. CPSInt
  158.             dc.l    0                    ; ln_Succ
  159.             dc.l    0                    ; ln_Pred
  160.             dc.b    NT_INTERRUPT        ; ln_Type
  161.             dc.b    0                    ; ln_Pri
  162.             dc.l    Name                ; ln_Name
  163.             dc.l    0                    ; is_Data
  164.             dc.l    ExtIntHandler       ; is_Code
  165.  
  166.             dc.w    0                    ; si_pad0
  167.             ds.b    CPSSubUnit_Size,0    ; si_Unit0 (except...)
  168.             dc.l    MPD0                ; si_Unit0+su_MPDA
  169.             dc.l    MPD1                ; si_Unit0+su_MPDB
  170.             ds.b    CPSSubUnit_Size,0    ; si_Unit1 (except...)
  171.             dc.l    MPD2                ; si_Unit1+su_MPDA
  172.             dc.l    MPD3                ; si_Unit1+su_MPDB
  173.  
  174. ****************************************************************
  175. *
  176. *   MidiDeviceData Functions
  177. *
  178. ****************************************************************
  179.  
  180. ****************************************************************
  181. *
  182. *   Init
  183. *
  184. *   FUNCTION
  185. *    Gets called by CAMD after being LoadSeg'ed.
  186. *
  187. *   INPUTS
  188. *    None
  189. *
  190. *   RESULTS
  191. *    TRUE if successful, FALSE on failure.
  192. *
  193. ****************************************************************
  194.  
  195. init_reg     reg a2/a6
  196.  
  197. Init        movem.l init_reg,-(sp)
  198.  
  199.             lea        CPSInt,a2                ; get base of data
  200.  
  201.             lea        MDD,a1
  202.             move.b    #2,mdd_NPorts(a1)        ; reset NPorts
  203.  
  204.             move.l    AbsExecBase,a6             ; A6 = SysBase
  205.  
  206.                 ; get Checkpoint card base
  207.             lea        ExpansionName,a1        ; open expansion.library
  208.             moveq    #0,d0
  209.             JSRLIB    OpenLibrary
  210.             move.l    d0,a6
  211.             tst.l    d0
  212.             beq        init_ret
  213.  
  214.             suba.l    a0,a0                ; find the card (2055/0)
  215.             move.l    #2055,d0
  216.             move.l    a0,d1
  217.             JSRLIB    FindConfigDev
  218.             move.l    d0,a0
  219.             tst.l    d0
  220.             beq.s    1$
  221.  
  222.                                         ; save card base
  223.             move.l    cd_BoardAddr(a0),si_Unit0+su_CardBase(a2)
  224.  
  225.             move.l    #2055,d0            ; look for a second board
  226.             moveq    #0,d1                ; a0 already has right value
  227.             JSRLIB    FindConfigDev
  228.             move.l    d0,a0
  229.             tst.l    d0
  230.             beq.s    1$
  231.  
  232.                                         ; save second card base
  233.             move.l    cd_BoardAddr(a0),si_Unit1+su_CardBase(a2)
  234.  
  235.             lea        MDD,a1
  236.             addq.b    #2,mdd_NPorts(a1)    ; add two more Ports
  237.  
  238. 1$            move.l    a6,a1                ; close expansion.library
  239.             move.l    AbsExecBase,a6         ; A6 = SysBase
  240.             JSRLIB    CloseLibrary
  241.  
  242.             move.l    si_Unit0+su_CardBase(a2),d0    ; test (and set) Z-bit
  243.             beq.s    init_ret
  244.  
  245.             moveq    #1,d0                ; return TRUE
  246. init_ret
  247.             movem.l (sp)+,init_reg
  248.             rts
  249.  
  250. ExpansionName
  251.             dc.b    'expansion.library',0
  252.             ds.w    0
  253.  
  254. ****************************************************************
  255. *
  256. *   Expunge
  257. *
  258. *   FUNCTION
  259. *    Gets called by CAMD immediately before being UnLoadSeg'ed.
  260. *
  261. *   INPUTS
  262. *    None
  263. *
  264. *   RESULTS
  265. *    None
  266. *
  267. ****************************************************************
  268.  
  269. Expunge
  270.             rts
  271.  
  272.  
  273. ****************************************************************
  274. *
  275. *   OpenPort
  276. *
  277. *   FUNCTION
  278. *    Open a MIDI port.
  279. *
  280. *   INPUTS
  281. *    D0.b - Port number (should be 0 or 1 for this driver [or 2/3])
  282. *    A0 - Xmit function
  283. *    A1 - Recv function
  284. *    A2 - Data
  285. *
  286. *   RESULT
  287. *    D0 - pointer to MidiPortData structure.
  288. *
  289. ****************************************************************
  290.  
  291. op_reg    reg a3/a5/a6/d2
  292.  
  293. OpenPort
  294.             movem.l op_reg,-(sp)
  295.  
  296.             move.l    AbsExecBase,a6         ; A6 = SysBase
  297.             lea        CPSInt,a5            ; get base of interrupt structure
  298.             lea        si_Unit0(a5),a3        ; point to first unit
  299.  
  300.             moveq    #0,d2
  301.             move.b    d0,d2                ; save port # in d2 (we have plans...)
  302.             beq        openportA
  303.             cmp.b    #1,d0
  304.             beq        openportB
  305.  
  306.             lea        si_Unit1(a5),a3        ; point to second unit
  307.             cmp.b    #2,d0
  308.             bne        openportB
  309.  
  310. openportA
  311.             btst.b    d2,si_IsActive(a5)    ; see if already open
  312.             bne.s    is_open                ; yep, that's a no-no
  313.  
  314.             move.l    a0,su_XmitA(a3)        ; save func/data pointers
  315.             move.l    a1,su_RecvA(a3)
  316.             move.l    a2,su_DataA(a3)
  317.  
  318.             move.l    su_CardBase(a3),a0
  319.                 ; reset channel A mode, disable rec & xmit
  320.             move.b    #CR_MR1|CR_TD|CR_RCD,cps_CommandRegA(a0)
  321.                 ; RTS off, RxRDY IRQ, Char Err, No Parity, 8 bits
  322.             move.b    #MR1_8DB|MR1_NP,cps_ModeRegA(a0)
  323.                 ; no special, 1.000 stop bit length
  324.             move.b    #MR2_1SB,cps_ModeRegA(a0)
  325.                 ; use timer
  326.             move.b    #CSR_TIM|(CSR_TIM<<4),cps_ClockSelRegA(a0)
  327.                 ; enable rec & xmit, clear error bits
  328.             move.b    #CR_RCE|CR_TE|CR_RE,cps_CommandRegA(a0)
  329.                 ; set clock source
  330.             move.b    #ACR_IP2,cps_AuxControlReg(a0)
  331.                 ; set the timer constant for MIDI
  332.             move.w    #4,d1
  333.             movep.w    d1,cps_CountTimerUR(a0)
  334.  
  335.             bsr    openportint
  336.             bset.b    d2,si_IsActive(a5)
  337.             bset.b    #ISR_RRA,su_IntMask(a3)
  338.  
  339.                 ; enable port A receice interrupt
  340.             move.l    su_CardBase(a3),a0
  341.             move.b    su_IntMask(a3),cps_IntMaskReg(a0)
  342.  
  343.                 ; return MPD
  344.             move.l    su_MPDA(a3),d0
  345.             bra.s    op_ret
  346.  
  347. openportB
  348.             btst.b    d2,si_IsActive(a5)    ; see if already open
  349.             bne.s    is_open                ; yep, that's a no-no
  350.  
  351.             move.l    a0,su_XmitB(a3)        ; save func/data pointers
  352.             move.l    a1,su_RecvB(a3)
  353.             move.l    a2,su_DataB(a3)
  354.  
  355.             move.l    su_CardBase(a3),a0
  356.                 ; reset channel A mode, disable rec & xmit
  357.             move.b    #CR_MR1|CR_TD|CR_RCD,cps_CommandRegB(a0)
  358.                 ; RTS off, RxRDY IRQ, Char Err, No Parity, 8 bits
  359.             move.b    #MR1_8DB|MR1_NP,cps_ModeRegB(a0)
  360.                 ; no special, 1.000 stop bit length
  361.             move.b    #MR2_1SB,cps_ModeRegB(a0)
  362.                 ; use timer
  363.             move.b    #CSR_TIM|(CSR_TIM<<4),cps_ClockSelRegB(a0)
  364.                 ; enable rec & xmit, clear error bits
  365.             move.b    #CR_RCE|CR_TE|CR_RE,cps_CommandRegB(a0)
  366.                 ; set clock source
  367.             move.b    #ACR_IP2,cps_AuxControlReg(a0)
  368.                 ; set the timer constant for MIDI
  369.             move.w    #4,d1
  370.             movep.w    d1,cps_CountTimerUR(a0)
  371.  
  372.             bsr    openportint
  373.             bset.b    d2,si_IsActive(a5)
  374.             bset.b    #ISR_RRB,su_IntMask(a3)
  375.  
  376.                 ; enable port B receive interrupt
  377.             move.l    su_CardBase(a3),a0
  378.             move.b    su_IntMask(a3),cps_IntMaskReg(a0)
  379.  
  380.                 ; return MPD
  381.             move.l    su_MPDB(a3),d0
  382.  
  383. op_ret        movem.l (sp)+,op_reg
  384.             rts
  385.  
  386. is_open            ; error, port already open
  387.             moveq    #0,d0
  388.             movem.l (sp)+,op_reg
  389.             rts
  390.  
  391. openportint
  392.             DISABLE a0
  393.  
  394.             tst.b    si_IsActive(a5)        ; if int active, don't add again!!
  395.             bne.s    int_is_on
  396.  
  397.                     ; add EXTER interrupt vector
  398.             moveq    #INTB_EXTER,d0
  399.             move.l    a5,a1                ; CPSInt structure
  400.             JSRLIB    AddIntServer
  401.  
  402.             move.w    #INTF_SETCLR!INTF_EXTER,custom+intena   ; enable EXTER
  403.  
  404. int_is_on
  405.             ENABLE a0
  406.             rts
  407.  
  408. ****************************************************************
  409. *
  410. *   ClosePort
  411. *
  412. *   FUNCTION
  413. *    Close a MIDI port.
  414. *
  415. *   INPUTS
  416. *    D0.b - Port number (0 or 1 [or 2 or 3] for this driver).
  417. *
  418. *   RESULT
  419. *    None
  420. *
  421. ****************************************************************
  422.  
  423. cp_reg    reg a3/a5/a6/d2
  424.  
  425. ClosePort
  426.             movem.l cp_reg,-(sp)
  427.  
  428.             move.l    AbsExecBase,a6         ; A6 = SysBase
  429.             lea        CPSInt,a5            ; get base of interrupt structure
  430.             lea        si_Unit0(a5),a3        ; point to first unit
  431.  
  432.             moveq    #0,d2
  433.             move.b    d0,d2                ; save port # in d2
  434.             beq        closeportA
  435.             cmp.b    #1,d0
  436.             beq        closeportB
  437.  
  438.             lea        si_Unit1(a5),a3        ; point to second unit
  439.             cmp.b    #2,d0
  440.             bne        closeportB
  441.  
  442. closeportA
  443.             move.l    su_CardBase(a3),a0
  444.  
  445.                 ; expect a0 = CardBase
  446.             bsr        WaitSerialA
  447.  
  448.             bclr.b    #ISR_RRA,su_IntMask(a3)
  449.             bclr.b    #ISR_TRA,su_IntMask(a3)
  450.             move.b    su_IntMask(a3),cps_IntMaskReg(a0)
  451.             bclr.b    d2,si_IsActive(a5)
  452.             move.b    si_IsActive(a5),d0
  453.             bne.s    closed
  454.  
  455. closeint        ; remove EXTER interrupt handler
  456.             moveq    #INTB_EXTER,d0
  457.             move.l    a5,a1                ; CPSInt structure
  458.             JSRLIB    RemIntServer
  459.             bra.s    closed
  460.  
  461. closeportB
  462.             move.l    su_CardBase(a3),a0
  463.  
  464.                 ; expect a0 = CardBase
  465.             bsr        WaitSerialB
  466.  
  467.             bclr.b    #ISR_RRB,su_IntMask(a3)
  468.             bclr.b    #ISR_TRB,su_IntMask(a3)
  469.             move.b    su_IntMask(a3),cps_IntMaskReg(a0)
  470.             bclr.b    d2,si_IsActive(a5)
  471.             move.b    si_IsActive(a5),d0
  472.             beq.s    closeint
  473.  
  474. closed        movem.l (sp)+,cp_reg
  475.             rts
  476.  
  477.  
  478. ****************************************************************
  479. *
  480. *   ActivateXmit
  481. *
  482. *   FUNCTION
  483. *    Activate the transmit interrupt.  There is normally
  484. *    one of these functions for every port in order to
  485. *    reduce table lookup time.
  486. *
  487. *   INPUTS
  488. *    A2 - Data passed to OpenPort() (not used here)
  489. *
  490. *   RESULT
  491. *    None
  492. *
  493. ****************************************************************
  494.  
  495. ActivateXmitA
  496.                 ; enable port A xmit interrupt
  497.             lea        CPSInt+si_Unit0,a1
  498.             move.l    su_CardBase(a1),a0
  499.             bset.b    #ISR_TRA,su_IntMask(a1)
  500.             move.b    su_IntMask(a1),cps_IntMaskReg(a0)
  501.             rts
  502.  
  503. ActivateXmitB
  504.                 ; enable port B xmit interrupt
  505.             lea        CPSInt+si_Unit0,a1
  506.             move.l    su_CardBase(a1),a0
  507.             bset.b    #ISR_TRB,su_IntMask(a1)
  508.             move.b    su_IntMask(a1),cps_IntMaskReg(a0)
  509.             rts
  510.  
  511. ActivateXmitC
  512.                 ; enable port A xmit interrupt
  513.             lea        CPSInt+si_Unit1,a1
  514.             move.l    su_CardBase(a1),a0
  515.             bset.b    #ISR_TRA,su_IntMask(a1)
  516.             move.b    su_IntMask(a1),cps_IntMaskReg(a0)
  517.             rts
  518.  
  519. ActivateXmitD
  520.                 ; enable port B xmit interrupt
  521.             lea        CPSInt+si_Unit1,a1
  522.             move.l    su_CardBase(a1),a0
  523.             bset.b    #ISR_TRB,su_IntMask(a1)
  524.             move.b    su_IntMask(a1),cps_IntMaskReg(a0)
  525.             rts
  526.  
  527. ****************************************************************
  528. *
  529. *   WaitSerial
  530. *
  531. *   FUNCTION
  532. *    Wait until Port TXR bit is set.  This
  533. *    indicates that the serial shift register is empty.
  534. *
  535. *   INPUTS
  536. *    a0 = CardBase
  537. *
  538. *   RESULTS
  539. *    None
  540. *
  541. *   NOTE
  542. *    This function busy waits!!!  It should only be called
  543. *    once the transmit queue has emptied such that no more
  544. *    than 320us is wasted here.
  545. *
  546. ****************************************************************
  547.  
  548. WaitSerialA
  549.             move.b    cps_StatusRegA(a0),d0
  550.             btst.l    #SRB_TXR,d0
  551.             beq        WaitSerialA
  552.  
  553.             rts
  554.  
  555. WaitSerialB
  556.             move.b    cps_StatusRegB(a0),d0
  557.             btst.l    #SRB_TXR,d0
  558.             beq        WaitSerialB
  559.  
  560.             rts
  561.  
  562. ****************************************************************
  563. *
  564. *   Serial Interrupt Handlers
  565. *
  566. ****************************************************************
  567.  
  568.  
  569. ****************************************************************
  570. *
  571. *   ExtIntHandler
  572. *
  573. *   FUNCTION
  574. *    Checkpoint serial card interrupt handler.  Handles both
  575. *    receive and transmit.
  576. *
  577. ****************************************************************
  578.  
  579. ext_reg reg a2/a3/a5/d2
  580.  
  581. ExtIntHandler
  582.             movem.l ext_reg,-(sp)
  583.  
  584.             moveq    #0,d2                    ; init result to "didn't handle"
  585.  
  586.             lea        CPSInt+si_Unit0,a3        ; get first unit
  587.             bsr        extloop                    ; check out interrupts
  588.  
  589.             lea        CPSInt+si_Unit1,a3        ; get second unit
  590.             bsr        extloop                    ; check it out too
  591.  
  592.             move.l    d2,d0
  593.             movem.l (sp)+,ext_reg
  594.             rts
  595.  
  596. extloop        move.b    su_IntMask(a3),d0        ; get ints we care about
  597.             beq.s    extdone                    ; if zero, not ours!
  598.  
  599.             move.l    su_CardBase(a3),a1
  600.             and.b    cps_IntStatusReg(a1),d0    ; AND in card interrupts
  601.  
  602.             btst    #ISR_RRA,d0                ; port A receiver interrupt?
  603.             beq.s    not_recA
  604.  
  605.             move.l    su_RecvA(a3),a5            ; return user supplied values
  606.             move.l    su_DataA(a3),a2
  607.  
  608.             moveq    #0,d0
  609.             move.b    cps_RecBufferA(a1),d0    ; get a received byte
  610.  
  611.             move.b    cps_StatusRegA(a1),d1    ; get the status of channel
  612.             and.b    #SR_OVR|SR_FRA,d1        ; framing/overrun error?
  613.             beq.s    1$                        ; no, skip
  614.  
  615.             or.w    #$8000,d0                ; report an error
  616.             move.b    #CR_RE,cps_CommandRegA(a1)    ; clear error bits
  617.  
  618. 1$            jsr        (a5)
  619.             moveq    #1,d2
  620.             bra.s    extloop
  621.  
  622. not_recA    btst    #ISR_RRB,d0            ; port B receiver interrupt?
  623.             beq.s    not_recB
  624.  
  625.             move.l    su_RecvB(a3),a5
  626.             move.l    su_DataB(a3),a2
  627.  
  628.             moveq    #0,d0
  629.             move.b    cps_RecBufferB(a1),d0    ; get a received byte
  630.  
  631.             move.b    cps_StatusRegB(a1),d1    ; get the status of channel
  632.             and.b    #SR_OVR|SR_FRA,d1        ; framing/overrun error?
  633.             beq.s    1$                        ; no, skip
  634.  
  635.             or.w    #$8000,d0                ; report an error
  636.             move.b    #CR_RE,cps_CommandRegB(a1)    ; clear error bits
  637.  
  638. 1$            jsr        (a5)
  639.             moveq    #1,d2
  640.             bra.s    extloop
  641.  
  642. not_recB    btst    #ISR_TRA,d0            ; port A xmit interrupt?
  643.             beq.s    not_xmitA
  644.  
  645.             move.l    su_XmitA(a3),a5
  646.             move.l    su_DataA(a3),a2
  647.             jsr        (a5)                       ; returns byte in D0, last byte flag in D1
  648.  
  649.             move.l    su_CardBase(a3),a1
  650.             move.b    d0,cps_XmitBufferA(a1)
  651.  
  652.             moveq    #1,d2
  653.  
  654.             tst.b    d1            ; is last byte?
  655.             beq.s    extloop
  656.  
  657.             bclr    #ISR_TRA,su_IntMask(a3)
  658.             move.b    su_IntMask(a3),cps_IntMaskReg(a1)
  659.             bra.s    extdone
  660.  
  661. not_xmitA    btst    #ISR_TRB,d0            ; port B xmit interrupt?
  662.             beq.s    extdone
  663.  
  664.             move.l    su_XmitB(a3),a5
  665.             move.l    su_DataB(a3),a2
  666.             jsr        (a5)                       ; returns byte in D0, last byte flag in D1
  667.  
  668.             move.l    su_CardBase(a3),a1
  669.             move.b    d0,cps_XmitBufferB(a1)
  670.  
  671.             moveq    #1,d2
  672.  
  673.             tst.b    d1            ; is last byte?
  674.             beq        extloop
  675.  
  676.             bclr    #ISR_TRB,su_IntMask(a3)
  677.             move.b    su_IntMask(a3),cps_IntMaskReg(a1)
  678.  
  679. extdone        rts
  680.  
  681.             end
  682.